using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Reflection;

namespace Adge.Web.UI
{
	/// <summary>
	/// A class with two static methods <see cref="BindObjectToControls"/> and <see cref="BindControlsToObject"/>
	/// used to bind the values of and object's properties with web <see cref="Control"/>s that have their
	/// ID property set to the name of the object property.
	/// </summary>
	public class FormBinding {
		/// <summary>
		/// Binds an object's properties to <see cref="Control"/>s with the same ID as the propery name. 
		/// </summary>
		/// <param name="obj">The object whose properties are being bound to forms Controls</param>
		/// <param name="container">The control in which the form Controls reside (usually a Page or ContainerControl)</param>
		public static void BindObjectToControls(object obj, Control container) {
			if (obj == null) return;
			
			// Get the properties of the business object
			//
			Type objType = obj.GetType();
			PropertyInfo[] objPropertiesArray = objType.GetProperties();

			foreach (PropertyInfo objProperty in objPropertiesArray) {

				Control control = container.FindControl(objProperty.Name);
                if (control == null)
                {
                    continue;
                }
				
				// handle ListControls (DropDownList, CheckBoxList, RadioButtonList)
				//
				if (control is ListControl) {
					ListControl listControl = (ListControl) control;
					string propertyValue = objProperty.GetValue(obj, null).ToString();
					ListItem listItem = listControl.Items.FindByValue(propertyValue);
					if (listItem != null) listItem.Selected = true;
				
				} else {
					// get the properties of the control
					//
					Type controlType = control.GetType();
					PropertyInfo[] controlPropertiesArray = controlType.GetProperties();

					// test for common properties
					//
					bool success = false;
					success = FindAndSetControlProperty(obj, objProperty, control, controlPropertiesArray, "Checked", typeof(bool) );
						
					if (!success) 
						success = FindAndSetControlProperty(obj, objProperty, control, controlPropertiesArray, "SelectedDate", typeof(DateTime) );

					if (!success) 
						success = FindAndSetControlProperty(obj, objProperty, control, controlPropertiesArray, "Value", typeof(String) );
						
					if (!success) 
						success = FindAndSetControlProperty(obj, objProperty, control, controlPropertiesArray, "Text", typeof(String) );
						
				}
			}
		}
		
		/// <summary>
		/// Looks for a property name and type on a control and attempts to set it to the value in an object's property 
		/// of the same name.
		/// </summary>
		/// <param name="obj">The object whose properties are being retrieved</param>
		/// <param name="objProperty">The property of the object being retrieved</param>
		/// <param name="control">The control whose ID matches the object's property name.</param>
		/// <param name="controlPropertiesArray">An array of the control's properties</param>
		/// <param name="propertyName">The name of the Control property being set</param>
		/// <param name="type">The correct type for the Control property</param>
		/// <returns>Boolean for whether the property was found and set</returns>
		private static bool FindAndSetControlProperty(object obj, PropertyInfo objProperty, Control control, PropertyInfo[] controlPropertiesArray, string propertyName, Type type) {
			// iterate through control properties
			//
			foreach (PropertyInfo controlProperty in controlPropertiesArray) {
				// check for matching name and type
				//
				if (controlProperty.Name == propertyName && controlProperty.PropertyType == type) {
					// set the control's property to the business object property value
					//
					controlProperty.SetValue(control, Convert.ChangeType( objProperty.GetValue(obj, null), type) , null);
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// Binds your the values in <see cref="Control"/>s to a business object.
		/// </summary>
		/// <param name="obj">The object whose properties are being bound to Control values</param>
		/// <param name="container">The control in which the form Controls reside (usually a Page or ContainerControl)</param>
		public static void BindControlsToObject(object obj, Control container) {
			if (obj == null) return;
			
			// Get the properties of the business object
			//			
			Type objType = obj.GetType();
			PropertyInfo[] objPropertiesArray = objType.GetProperties();

			foreach (PropertyInfo objProperty in objPropertiesArray) {

				Control control = container.FindControl(objProperty.Name);
                if (control == null)
                {
                    continue;
                }
				if (control is ListControl) {
					ListControl listControl = (ListControl) control;
					if (listControl.SelectedItem != null)
						objProperty.SetValue(obj, Convert.ChangeType(listControl.SelectedItem.Value,objProperty.PropertyType), null);

				} else {
					// get the properties of the control
					//
					Type controlType = control.GetType();
					PropertyInfo[] controlPropertiesArray = controlType.GetProperties();

					// test for common properties
					//
					bool success = false;
					success = FindAndGetControlProperty(obj, objProperty, control, controlPropertiesArray, "Checked", typeof(bool) );

					if (!success) 
						success = FindAndGetControlProperty(obj, objProperty, control, controlPropertiesArray, "SelectedDate", typeof(DateTime) );

					if (!success) 
						success = FindAndGetControlProperty(obj, objProperty, control, controlPropertiesArray, "Value", typeof(String) );

					if (!success) 
						success = FindAndGetControlProperty(obj, objProperty, control, controlPropertiesArray, "Text", typeof(String) );
				}
			}
		}

		/// <summary>
		/// Looks for a property name and type on a control and attempts to set it to the value in an object's property 
		/// of the same name.
		/// </summary>
		/// <param name="obj">The object whose properties are being set</param>
		/// <param name="objProperty">The property of the object being set</param>
		/// <param name="control">The control whose ID matches the object's property name.</param>
		/// <param name="controlPropertiesArray">An array of the control's properties</param>
		/// <param name="propertyName">The name of the Control property being retrieved</param>
		/// <param name="type">The correct type for the Control property</param>
		/// <returns>Boolean for whether the property was found and retrieved</returns>
		private static bool FindAndGetControlProperty(object obj, PropertyInfo objProperty, Control control, PropertyInfo[] controlPropertiesArray, string propertyName, Type type) {
			// iterate through control properties
			//
			foreach (PropertyInfo controlProperty in controlPropertiesArray) {
				// check for matching name and type
				//
				if (controlProperty.Name == "Text" && controlProperty.PropertyType == typeof(String)) {
					// set the control's property to the business object property value
					//
					try {
						objProperty.SetValue(control, Convert.ChangeType( controlProperty.GetValue(obj, null), objProperty.PropertyType) , null);
						return true;
					} catch {
						// the data from the form control could not be converted to objProperty.PropertyType
						//
						return false;
					}
				}
			}
			return false;
		}
	}
}
